
#include "stdafx.h"

#include "desc_compiler/desc_compiler.h"

using namespace boost::python;

object TokenToObj(const dc::TOKEN & token) {
   switch (token.token) {
      case ds::TOKEN_INT:
         return object(atoi(token.value.CStr()));

      case ds::TOKEN_FLOAT:
         return object(atof(token.value.CStr()));
   
      default:
      {
         std::string val = token.value.CStr();
         if (token.token == ds::TOKEN_STRING) {
            val = "\"" + val + "\"";
         }
         return object(val);
      }
   }
}

struct DescAttribute {
   std::string name;
   list args;

   static list build(const dsVECTOR<dc::ATTRIB> & attribs) {
      list res;
      for (int k = 0; k < attribs.Length(); ++k) {
         DescAttribute * val = NULL;
         object o = DescAttribute::New(&val);

         const dc::ATTRIB & attr = attribs[k];
         val->name = attr.name.value.CStr();

         for (int k = 0; k < attr.args.Length(); ++k) {
            val->args.append(TokenToObj(attr.args[k]));
         }

         res.append(o);
      }

      return res;
   }

   CLASS_WRAPPER_BEGIN(DescAttribute)
      class_<DescAttribute> ("Attribute")
         .def_readonly("name",   &DescAttribute::name,   "Attribute name")
         .def_readonly("args",   &DescAttribute::args,   "Attribute arguments")
      ;
   CLASS_WRAPPER_END()
};

struct DescVar {
   std::string name;
   object      defVal;
   int         typeId;
   list        attribs;

   static list build(const dsVECTOR<dc::VAR> & vars) {
      list res;
      for (int k = 0; k < vars.Length(); ++k) {
         DescVar * val = NULL;
         object o = DescVar::New(&val);
         val->name   = vars[k].name.value.CStr();
         val->typeId = vars[k].type;
         val->defVal = TokenToObj(vars[k].value);
         val->attribs = DescAttribute::build(vars[k].attribs);

         res.append(o);
      }

      return res;
   }

   CLASS_WRAPPER_BEGIN(DescVar)
      class_<DescVar> ("Var")
         .def_readonly("name",   &DescVar::name,   "Variable name")
         .def_readonly("defVal", &DescVar::defVal, "Varibale default value")
         .def_readonly("typeId", &DescVar::typeId, "Variable type identifier")
         .def_readonly("attribs", &DescVar::typeId, "List of variable attributes")
      ;
   CLASS_WRAPPER_END()
};

struct DescTypeInfo {
   std::string name;
   std::string parent;
   std::string source;
   int         line;
   list        attribs;
   list        vars;

   void init(const dc::TYPE_INFO & ti, const dc::CONTEXT & ctxt) {
      name     = ti.name.value.CStr();
      source   = ti.source.CStr();
      line     = ti.name.line;
      if (ti.parentTypeId != -1) {
         parent   = ctxt.types[ti.parentTypeId].name.CStr();
      }
      attribs  = DescAttribute::build(ti.attribs);
      vars     = DescVar::build(ti.vars);
   }

   CLASS_WRAPPER_BEGIN(DescTypeInfo)
      class_<DescTypeInfo> ("TypeInfo")
         .def_readonly("name",      &DescTypeInfo::name,       "Type name")
         .def_readonly("parent",    &DescTypeInfo::parent,     "Parent type")
         .def_readonly("attribs",   &DescTypeInfo::attribs,    "Type attribute list")
         .def_readonly("vars",      &DescTypeInfo::vars,       "Type variable list")
         .def_readonly("source",    &DescTypeInfo::source,     "Type declaration source file name")
         .def_readonly("line",      &DescTypeInfo::line,       "Type declaration line")
      ;
   CLASS_WRAPPER_END()
};

struct DescType {
   std::string name;
   std::string type;
   int typeId;

   CLASS_WRAPPER_BEGIN(DescType)
      class_<DescType> ("Type")
         .def_readonly("name",   &DescType::name,     "Type name")
         .def_readonly("type",   &DescType::type,     "Type value (could be 'type', 'array' or 'set')")
         .def_readonly("typeId", &DescType::typeId,   "Subtype id (is valid if type == 'array' or type == 'set')")
      ;
   CLASS_WRAPPER_END()
};

struct DescCompilerContext {
   list desc_list;
   list struct_list;
   list type_list;

   void init(const dc::CONTEXT & ctxt) {
      for (int k = 0; k < ctxt.descs.Length(); ++k) {
         DescTypeInfo * ti = NULL;
         object d = DescTypeInfo::New(&ti);
         ti->init(ctxt.descs[k], ctxt);
         
         desc_list.append(d);
      }

      for (int k = 0; k < ctxt.structs.Length(); ++k) {
         DescTypeInfo * ti = NULL;
         object d = DescTypeInfo::New(&ti);
         ti->init(ctxt.structs[k], ctxt);

         struct_list.append(d);
      }

      for (int k = 0; k < ctxt.types.Length(); ++k) {
         DescType * type = NULL;
         object t = DescType::New(&type);
         type->name = ctxt.types[k].name.CStr();

         switch (ctxt.types[k].type) {
            case dc::VT_ARRAY:   type->type = "array";   break;
            case dc::VT_SET:     type->type = "set";     break;
            default:             type->type = "type";    break;
         }

         type->typeId = ctxt.types[k].typeId;

         type_list.append(t);
      }
   }

   CLASS_WRAPPER_BEGIN(DescCompilerContext)
      class_<DescCompilerContext>("CompilerContext")
         .def_readonly("descs",    &DescCompilerContext::desc_list,    "Desc's type information list")
         .def_readonly("structs",  &DescCompilerContext::struct_list,  "Struct's type information list")
         .def_readonly("types",    &DescCompilerContext::type_list,    "Type's information list")
      ;
   CLASS_WRAPPER_END()
};

struct DescCompiler {
   std::string errorMsg;

   object compile1(std::string textStr)
   {
      return compile(textStr, "<text>", 0);
   }

   object compile2(std::string textStr, std::string filename)
   {
      return compile(textStr, filename, 0);
   }

   object compile(std::string text, std::string filename, int line)
   {
      dc::COMPILER compiler;
      dc::CONTEXT ctxt;
      ctxt.file = filename.c_str();
      ctxt.line = line;

      if (!compiler.Parse(text.c_str(), ctxt)) {
         this->errorMsg = compiler.error.CStr();
         return object();
      }

      DescCompilerContext * c = NULL;
      object res = DescCompilerContext::New(&c);
      c->init(ctxt);
      return res;
   }

   CLASS_WRAPPER_BEGIN(DescCompiler)
      class_<DescCompiler>("Compiler")
         .def_readonly("errorMsg", &DescCompiler::errorMsg)
         .def("compile", &DescCompiler::compile2)
         .def("compile", &DescCompiler::compile1)
         .def("compile", &DescCompiler::compile)
      ;
   CLASS_WRAPPER_END()
};

BOOST_PYTHON_MODULE(DescCompiler)
{
   DescAttribute::Type();
   DescVar::Type();
   DescType::Type();
   DescTypeInfo::Type();
   DescCompilerContext::Type();
   DescCompiler::Type();
}
